#include "xstring.h"
#include "autodelete.hpp"
#include "common.h"

#include <cstring>
#include <cstdio>
#include <cstdint>

wstring &wstring::fromAnsi(const char *p)
{
    clear();
    int size = strlen(p) + 1;

    int new_size = ::MultiByteToWideChar(CP_ACP, 0, p, size, NULL, 0);
    if (!new_size)
    {
        E("MultiByteToWideChar");
        return *this;
    }
    AutoDeletes<wchar_t> q(new wchar_t[new_size]);
    new_size = ::MultiByteToWideChar(CP_ACP, 0, p, size, q, new_size);
    if (!new_size)
    {
        E("MultiByteToWideChar");
        return *this;
    }
    m_ptr = q.move();
    m_len = new_size;
    return *this;
}
wstring &wstring::FormatV(const wchar_t *fmt, va_list ap)
{
    clear();

    int len = _vscwprintf(fmt, ap);
    if(len < 0)
        return *this;

    size_t size = (size_t)len + 1;
    AutoDeletes<wchar_t> p(new wchar_t[size]);
    if(!p)
        return *this;
    len = vswprintf_s(p, size, fmt, ap);
    if(len < 0)
        return *this;
    m_ptr = p.move();
    m_len = size;
    return *this;
}
wstring &wstring::Format(const wchar_t *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    FormatV(fmt, ap);
    va_end(ap);
    return *this;
}
wstring &wstring::AppendFormatV(const wchar_t *fmt, va_list ap)
{
    wstring s;
    *this += s.FormatV(fmt, ap);
    return *this;
}
wstring &wstring::AppendFormat(const wchar_t *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    AppendFormatV(fmt, ap);
    va_end(ap);
    return *this;
}
static_assert(sizeof(SSIZE_T) == sizeof(void*), "error ssize_t");
wstring &wstring::replace(const wchar_t *from, const wchar_t *to)
{
    SSIZE_T fromlen = wcslen(from);
    SSIZE_T tolen = wcslen(to);
    SSIZE_T diff = tolen - fromlen;
    SSIZE_T n = 0;
    for(const wchar_t *o = wcsstr(m_ptr, from); o; o = wcsstr(o + fromlen, from))
        n++;
    if(!n)
        return *this;
    SSIZE_T new_size = m_len + diff * n;
    if(new_size < 1)
        return *this;
    wstring t;
    wchar_t *ptr = t.m_ptr = new wchar_t[t.m_len = new_size];
    const wchar_t *p;
    for(const wchar_t *o = wcsstr(p = m_ptr, from); o; o = wcsstr(p = o + fromlen, from))
    {
        if(o > p)
        {
            memcpy(ptr, p, (o - p) * sizeof(wchar_t));
            ptr += o - p;
        }
        memcpy(ptr, to, tolen * sizeof(wchar_t));
        ptr += tolen;
    }
    if(m_ptr + m_len > p)
    {
        memcpy(ptr, p, (m_ptr + m_len - p) * sizeof(wchar_t));
        ptr += m_ptr + m_len - p;
    }
    swap(t);
    return *this;
}
void wstring::swap(wstring &o)
{
    wchar_t *t = m_ptr;
    m_ptr = o.m_ptr;
    o.m_ptr = t;
    size_t n = m_len;
    m_len = o.m_len;
    o.m_len = n;
}
wstring::wstring(const wchar_t *s)
{
    if(s && *s)
    {
        m_len = wcslen(s) + 1;
        m_ptr = new wchar_t[m_len];
        memcpy(m_ptr, s, m_len * sizeof(wchar_t));
    }
    else
    {
        m_len = 0;
        m_ptr = nullptr;
    }
}
wstring &wstring::operator=(const wchar_t *s)
{
    clear();
    if(s && *s)
    {
        m_len = wcslen(s) + 1;
        m_ptr = new wchar_t[m_len];
        memcpy(m_ptr, s, m_len * sizeof(wchar_t));
    }
    return *this;
}
